﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Interfleet.SE.UICompiler.Core.ClosureCompiler;
using Interfleet.SE.UICompiler.Core.ClosureCompilerREST;
using Interfleet.SE.UICompiler.Core.YahooCssCompressor;
using Interfleet.SE.UICompiler.Core.Batch;
using Interfleet.SE.UICompiler.Core.YahooJavaScriptCompressor;

namespace Interfleet.SE.UICompiler.Core
{
    /// <summary>
    /// The entry point for programming the UI Compiler.NET. It is an implementation of an
    /// abstract factory.
    /// </summary>
    /// <example>
    /// <para>Build up a compiler agent, set arguments and compile:</para>
    /// <code>
    /// CompileAgentFactory agentFactory = null;
    /// bool closureCompilerLocal = true;
    /// bool closureCompilerREST = false;
    /// bool yahooCss = false;
    /// bool yahooJavascript = false;
    /// 
    /// if( closureCompilerLocal )
    /// {
    ///     agentFactory = CompileAgentFactory.CreateAgentFactory(CompileType.ClosureCompilerREST);
    /// }
    /// else if( closureCompilerREST )
    /// {
    ///     agentFactory = CompileAgentFactory.CreateAgentFactory(CompileType.ClosureCompilerREST);
    /// }
    /// else if( yahooCss )
    /// {
    ///     agentFactory = CompileAgentFactory.CreateAgentFactory(CompileType.YUICompressorCss);
    /// }
    /// else if( yahooJavascript )
    /// {
    ///     agentFactory = CompileAgentFactory.CreateAgentFactory(CompileType.YUICompressorJavaScript);
    /// }
    /// var arguments = agentFactory.CreateArguments();
    /// // set values on arguments, see Arguments class...
    /// var compilerAgent = agentFactory.CreateCompilerAgent(arguments);
    /// var result = compilerAgent.Compile();
    /// </code>
    /// </example>
    /// <seealso cref="Arguments"/>
    /// <see cref="CompilerAgent"/>
    public abstract class CompileAgentFactory
    {
        /// <summary>
        /// Creates an instance of a <see cref="CompilerAgent"/>
        /// </summary>
        /// <param name="arguments">Arguments for the compiler</param>
        /// <returns>A <see cref="CompilerAgent"/></returns>
        public abstract CompilerAgent CreateCompilerAgent(Arguments arguments);

        /// <summary>
        /// Creates an empty instance of <see cref="Arguments"/>.
        /// </summary>
        /// <returns>An empty instance of <see cref="Arguments"/></returns>
        public abstract Arguments CreateArguments();

        /// <summary>
        /// Returns the available <see cref="CompileOptions"/>
        /// </summary>
        /// <returns><see cref="CompileOptions"/> for the current <see cref="CompilerAgent"/></returns>
        public abstract CompileOptions GetCompileOptions();

        /// <summary>
        /// Creates a <see cref="CompileAgentFactory"/> corresponding to the
        /// supplied <paramref name="compileType">CompileType</paramref>
        /// </summary>
        /// <param name="compileType">The requested <see cref="CompileType"/></param>
        /// <returns>An instance of <see cref="CompileAgentFactory"/></returns>
        public static CompileAgentFactory CreateAgentFactory(CompileType compileType)
        {
            switch (compileType.Supplier)
            {
                case CompileSupplier.ClosureCompilerLocal:
                    return new CCCompilerAgentFactory();
                case CompileSupplier.ClosureCompilerREST:
                    return new CCRESTCompilerAgentFactory();
                case CompileSupplier.YUICompressorCss:
                    return new YCCompilerAgentFactory();
                case CompileSupplier.YUICompressorJavaScript:
                    return new YJCompilerAgentFactory();
                default:
                    throw new InvalidOperationException("Non supported CompileType: " + compileType.ToString());
            }
        }
        /// <summary>
        /// Enables compilation to and from streams
        /// </summary>
        /// <param name="streamCompilerType">The requested <see cref="StreamCompileType"/></param>
        /// <returns>A <see cref="StreamCompileAgent"/></returns>
        /// <example>
        /// <code>
        /// using System.IO;
        /// using System.Web;
        /// 
        /// Stream inputStream;
        /// // initialize input stream
        /// var compilerAgent = CompileAgentFactory.CreateStreamCompilerAgent(StreamCompilerType.YUICompressorCss);
        /// compilerAgent.Compile(inputStream, Response.OutputStream);
        /// </code>
        /// </example>
        public StreamCompilerAgent CreateStreamCompilerAgent(StreamCompileType streamCompilerType)
        {
            switch (streamCompilerType.Supplier)
            {
                case CompileSupplier.YUICompressorCss:
                    return new YCStreamCompilerAgent();
                case CompileSupplier.YUICompressorJavaScript:
                    return new YJStreamCompilerAgent();
                default:
                    throw new InvalidOperationException("Non supported stream compile type: " + streamCompilerType.ToString());
            }
        }

        /// <summary>
        /// <para>
        /// Runs a batchjob stored in the specified <paramref name="batchFilePath"/>
        /// </para>
        /// <para>
        /// The batchfile is a xml file containing the data needed to compile one or more files.
        /// </para>
        /// </summary>
        /// <example>
        /// Here is an example of a valid batchfile.
        /// <code lang="xml">
        /// &lt;Batch compileType=&quot;ClosureCompilerREST&quot; outputFile=&quot;c:\output.js&quot;&gt;
        /// &lt;Files&gt;
        /// &lt;File&gt;c:\inputfile1.js;&lt;/File&gt;
        /// &lt;File&gt;c:\inputfile2.js;&lt;/File&gt;
        /// &lt;/Files&gt;
        /// &lt;/Batch&gt;
        /// </code>
        /// <para>How to run a bach job</para>
        /// <code>
        /// var result = CompileAgentFactory.RunBatch(@"c:\myBatch.xml");
        /// if(result.Success)
        /// {
        ///     // handle result...
        /// }
        /// </code>
        /// </example>
        /// <param name="batchFilePath">Full path of the batch file.</param>
        /// <returns>A <see cref="CompileResult"/> containing the result</returns>
        public static CompileResult RunBatch(string batchFilePath)
        {
            var batchJob = new BatchJob(batchFilePath);
            var factory = CreateAgentFactory(batchJob.CompileType);
            var args = CreateArgumentsFromBatch(batchJob);
            return factory.CreateCompilerAgent(args).Compile();
        }

        /// <summary>
        /// Creates <see cref="Arguments"/> out of a <see cref="BatchJob"/>
        /// </summary>
        /// <param name="batchJob">A <see cref="BatchJob"/></param>
        /// <returns>An instance of <see cref="Arguments"/> containing parameters from the <see cref="BatchJob"/></returns>
        public static Arguments CreateArgumentsFromBatch(BatchJob batchJob)
        {
            var factory = CreateAgentFactory(batchJob.CompileType);
            var arguments = factory.CreateArguments();
            batchJob.DecorateArguments(arguments);
            return arguments;
        }

    }
}
